#include "led_zhuoyue.h"
#if USE_ZHUOYUE_LED
// std-lib
#include <stdlib.h>
#include <string.h>
// lib
#include "big_or_little_endian.h"

// ---------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------
// lib
#include "debug.h"

#define ZY_HEAP_MALLOC(ulSize)             malloc(ulSize)
#define ZY_HEAP_FREE(p)                    free(p)
#define ZY_DELAY(ms)                       usleep(ms)

#define ZY_SEND_CMD(cmd, len)              // ...

// ---------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------

#define LED_RESOLUTION_HOR                  128                                     // 水平分辨率
#define LED_RESOLUTION_VER                  64                                      // 垂直分辨率
#define LED_LINE_NUM                        4                                       // LED屏的行数
#define LED_FONT_SIZE                       16                                      // 字体大小
#define LED_FIXED_FRAMEHEADER               0xAA
#define LED_FIXED_ADDR                      0
#define LED_FIXED_FRAMEEND                  0x55
#define LED_NOT_INCLUDE_DATA_LEN            12

enum {                                      // 指令代号 指令含义 
    led_issue_program                       = 0x01,                                 // 0x01 下发节目
    led_del_program                         = 0x02,                                 // 0x02 删除节目
    led_set_dev_number                      = 0x20,                                 // 0x20 设置设备编号
    led_set_net_parm                        = 0x22,                                 // 0x22 设置网络参数
    led_send_heartbeat                      = 0x28,                                 // 0x28 发送心跳包
    led_set_time                            = 0x61,                                 // 0x61 设置时间
    led_set_screen_parm                     = 0x63,                                 // 0x63 设置屏参
    led_set_part_parm                       = 0x65,                                 // 0x65 设置分区参数
    led_set_brightness                      = 0x67,                                 // 0x67 设置亮度
    led_set_baud_rate                       = 0x69,                                 // 0x69 设置波特率
    led_set_io_state                        = 0x6A,                                 // 0x6A 设置IO状态
    led_set_sw_screen                       = 0x6C,                                 // 0x6C 设置开关屏
};
typedef uint8_t command_code_t;

enum {                                      // 指令代号 指令含义
    led_recv_success                        = 0x00,                                // 0x00 执行成功
    led_recv_error                          = 0x01,                                // 0x01 指令错误
    led_recv_len_err                        = 0x02,                                // 0x02 包长度错误
    led_recv_parm_err                       = 0x03,                                // 0x03 参数设置错误
};
typedef uint8_t receive_code_t;

typedef struct {
    // 基础配置
    uint16_t hwHor;                         // 水平分辨率
    uint16_t hwVer;                         // 垂直分辨率
    uint8_t  byFontSize;                    // 字体大小
    uint8_t  byPixelPitch;                  // 像素间距
    
    // 推导参数
    uint8_t  byRowSum;                      // 行数
    uint8_t  byRowHeight;                   // 行高
    uint8_t  byRemainder;                   // 余数（行高）
    uint8_t  byScanMode;                    // 扫描方式
} led_scr_parm_t;

static led_scr_parm_t s_tLedParm = {
    .hwHor = LED_RESOLUTION_HOR,
    .hwVer = LED_RESOLUTION_VER,
    .byFontSize = LED_FONT_SIZE,
};

/// @brief 计算校验和
/// @param pData 
/// @param hwDataLen 
/// @return 校验和
static uint16_t led_calculate_checksum(uint16_t hwInitValue, uint8_t *pData, uint16_t hwDataLen) {
    #if IS_LITTLE_ENDIAN
    uint32_t uChecksum = __builtin_bswap16(hwInitValue);
    #else   // IS_LITTLE_ENDIAN
    uint32_t uChecksum = hwInitValue;
    #endif  // IS_LITTLE_ENDIAN

    for (uint16_t i = 0; i < hwDataLen; i++) {
        uChecksum += pData[i];
    }
    if (uChecksum > 0xFFFF) {
        uChecksum = ~uChecksum;
        uChecksum += 1;
    }
    uint16_t hwRet = (uint16_t)(uChecksum & 0xFFFF);

    #if IS_LITTLE_ENDIAN
    return __builtin_bswap16(hwRet);
    #else   // IS_LITTLE_ENDIAN
    return hwRet;
    #endif  // IS_LITTLE_ENDIAN
}

/// @brief 发送帧函数
/// @param byOpeCode 操作码
/// @param byRetFlag 返回标志位（是否需要返回）
/// @param pData 数据指针
/// @param hwDataLen 数据长度
static void led_send_frame_zhuoyue(uint8_t byOpeCode, bool bRetFlag, uint8_t *pData, uint16_t hwDataLen) {
    uint8_t aSendDataHeader[7] = {
        LED_FIXED_FRAMEHEADER, 
        0,                                  // 帧长度
        0,
        LED_FIXED_ADDR,
        byOpeCode,
        bRetFlag,
        0,                                  // 协议预留，不用管
    };
    uint8_t aSendDataEnd[3] = {
        0,                                  // 校验和
        0,
        LED_FIXED_FRAMEEND,
    };
    uint16_t *phwFrameLen = (uint16_t *)(aSendDataHeader + 1);
    uint16_t *phwChecksum = (uint16_t *)(aSendDataEnd + 0);
    uint16_t  hwTitleLen = sizeof(aSendDataHeader) + hwDataLen + sizeof(aSendDataEnd);
    #if IS_LITTLE_ENDIAN
    *phwFrameLen = __builtin_bswap16(hwTitleLen);
    #else   // IS_LITTLE_ENDIAN
    *phwFrameLen = hwTitleLen;
    #endif  // IS_LITTLE_ENDIAN
    *phwChecksum = led_calculate_checksum(0, aSendDataHeader, sizeof(aSendDataHeader));
    *phwChecksum = led_calculate_checksum(*phwChecksum, pData, hwDataLen);
    uint8_t  *pSendData = ZY_HEAP_MALLOC(hwTitleLen);
    uint8_t  *p = pSendData;
    memcpy(p, aSendDataHeader, sizeof(aSendDataHeader));
    p += sizeof(aSendDataHeader);
    memcpy(p, pData, hwDataLen);
    p += hwDataLen;
    memcpy(p, aSendDataEnd, sizeof(aSendDataEnd));
    // _DEBUG_PRINTF_HEX(DBG_STP, pSendData, hwTitleLen, "send led data len=%d, data:\n", hwTitleLen);
    ZY_SEND_CMD(pSendData, hwTitleLen);
    // 直接发送数据
    ZY_DELAY(50);
    // 等待一会
    ZY_HEAP_FREE(pSendData);
}

// ---------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------

/// @brief 初始化屏幕参数
/// @param hwHor 屏幕宽度（像素）
/// @param hwVer 屏幕高度（像素）
/// @param byFontSize 字体大小（像素）
/// @param byPixelPitch 像素间距（P?）
void led_init_parm_zhuoyue(uint16_t hwHor, uint16_t hwVer, uint8_t byFontSize, uint8_t byPixelPitch) {
    if ((hwHor != 0) || (hwVer != 0)) {
        s_tLedParm.hwHor = hwHor;
        s_tLedParm.hwVer = hwVer;
        s_tLedParm.byFontSize = byFontSize;
    }
    // 推导参数
    s_tLedParm.byRowSum = s_tLedParm.hwVer / byFontSize;
    s_tLedParm.byRowHeight = s_tLedParm.hwVer / s_tLedParm.byRowSum;
    s_tLedParm.byRemainder = s_tLedParm.hwVer % s_tLedParm.byRowSum;
    switch (byPixelPitch) {
        // 0x61：1/16 扫描，直行走线，一路数据带16行。
        // 0x81：1/8 扫描，直行走线，一路数据带8行。
        // 0x82：2/8 扫描。
        // 0x83：3/8 扫描。
        // 0x41：1/4 扫描，四扫下行，一路数据带16行。
        // 0x42：2/4 扫描。
        // 0x43：3/4 扫描。
        case P3_75: s_tLedParm.byScanMode = 0x61;
        case P10: s_tLedParm.byScanMode = 0x41;
        default: s_tLedParm.byScanMode = 0x61;
    }
}

/// @brief 设置屏幕
void led_set_screen_config_zhuoyue(void) {
    uint8_t  aData[7] = {0};
    uint16_t hwDataPonitNum = 0;
    aData[hwDataPonitNum++] = s_tLedParm.hwHor / 8;                                 // 屏宽
    aData[hwDataPonitNum++] = s_tLedParm.hwVer / 8;                                 // 屏高
    aData[hwDataPonitNum++] = 1;                                                    // 颜色 0x01：单色 0x02：双色
    aData[hwDataPonitNum++] = 0;                                                    // 数据极性 0x00：低 0x01：高
    aData[hwDataPonitNum++] = 1;                                                    // OE极性 0x00：低 0x01：高
                                                                                    // 扫描类型
                                                                                    // 0x61：1/16 扫描，直行走线，一路数据带16行。
                                                                                    // 0x81：1/8 扫描，直行走线，一路数据带8行。
                                                                                    // 0x82：2/8 扫描。
                                                                                    // 0x83：3/8 扫描。
                                                                                    // 0x41：1/4 扫描，四扫下行，一路数据带16行。
                                                                                    // 0x42：2/4 扫描。
    aData[hwDataPonitNum++] = s_tLedParm.byScanMode;                                // 0x43：3/4 扫描。
    aData[hwDataPonitNum++] = 0;                                                    // 预留
    led_send_frame_zhuoyue(led_set_screen_parm, false, aData, sizeof(aData));
}

/// @brief 设置分区
void led_set_area_config_zhuoyue(void) {
    uint8_t  aData[64] = {0};                                                       // 最多8行屏？ 8*4+1=33
    uint16_t hwDataPonitNum = 0;
    aData[hwDataPonitNum++] = LED_LINE_NUM;                                         // 显示屏分区的个数N
    for (uint16_t i = 0; i < LED_LINE_NUM; i++) {
        aData[hwDataPonitNum++] = 0;                                                // 分区X坐标
        aData[hwDataPonitNum++] = (s_tLedParm.byFontSize * i) / 8;                  // 分区Y坐标
        aData[hwDataPonitNum++] = s_tLedParm.hwHor / 8;                             // 分区宽
        aData[hwDataPonitNum++] = s_tLedParm.byFontSize / 8;                        // 分区高
    }
    led_send_frame_zhuoyue(led_set_part_parm, false, aData, hwDataPonitNum);
}

/// @brief 设置分区显示内容
/// @param hwAreaNum 分区序号
/// @param pData 分区显示内容数据
/// @param hwDataLen 数据长度
void led_set_area_display_content_zhuoyue(uint16_t hwAreaNum, uint8_t *pData, uint16_t hwDataLen) {
    uint8_t  aData[] = {
        hwAreaNum + 1,                      // 节目编号
        hwAreaNum + 1,                      // 分区号
        0,                                  // 显示方式
                                            // 0x00：FLASH显示（掉电保存，顺序显示） 
                                            // 0x01：RAM显示（掉电不保存，优先显示）
        0,                                  // 显示时间
                                            // 0x00：按照节目时间显示； 
                                            // 0x01~0xff：按照规定时间显示 
        0,                                  // 动画方式
                                            // 0x00：立即显示
                                            // 0x01：左移
                                            // 0x02：上移
                                            // 0x03：下移
        1,                                  // 移动速度-数值越大移动越快
        5,                                  // 显示页面的停留时间，单位S
        0,                                  // 字体大小
                                            // 0x00:16 点宋体
                                            // 0x01:24 点宋体
                                            // 0x02:32 点宋体
                                            // 0x03:12 点宋体(特殊项目)
        1,                                  // 颜色
                                            // 0x01：红色
                                            // 0x02：绿色
                                            // 0x03：黄色
                                            // 0x04：蓝色
                                            // 0x05：紫色
                                            // 0x06：青色
                                            // 0x07：白色
        0,                                  // 语音标志0：文字；1：语音+文字；其他未定义；（语音：RAM显示只播一次，FLSH显示重复播放） 
        0,                                  // 发音人
        0,                                  // 语速 
        0,                                  // 语调
        0,                                  // 音量
        0,                                  // 预留
        hwDataLen,                          // 数据长度
    };
    // 检查数据并修改
    // _DEBUG_PRINT(DBG_ERR, "Data processing is required for cases where the data is too long.\n");
    // 准备数据
    uint16_t  hwTitleLen = sizeof(aData) + hwDataLen;
    uint8_t  *pDataBuffer = ZY_HEAP_MALLOC(hwTitleLen);
    uint8_t  *p = pDataBuffer;
    memcpy(p, aData, sizeof(aData));
    p += sizeof(aData);
    memcpy(p, pData, hwDataLen);
    p += hwDataLen;
    // _DEBUG_PRINTF_HEX(DBG_STP, pDataBuffer, hwTitleLen, "send led data len=%d, data:\n", hwTitleLen);
    led_send_frame_zhuoyue(led_issue_program, false, pDataBuffer, hwTitleLen);
    ZY_HEAP_FREE(pDataBuffer);
}

#endif  // USE_ZHUOYUE_LED

